package org.springframework.beans.factory;

import org.springframework.beans.BeansException;
import org.springframework.beans.PropertyValue;
import org.springframework.beans.PropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.core.io.DefaultResourceLoader;
import org.springframework.core.io.Resource;
import org.springframework.util.StringValueResolver;

import java.io.IOException;
import java.util.Properties;

/**
 * @Name PropertyPlaceholderConfigurer
 * @Description: Bean 容器后处理器实现类
 * 描述信息：使用 properties 属性文件中的配置项替换 Xml 配置文件中的占位符。
 * @Version v1.0.0
 * @Author: lichao
 * @Date: 2024-03-19 17:34:09
 **/
public class PropertyPlaceholderConfigurer implements BeanFactoryPostProcessor {
    public static final String PLACEHOLDER_PREFFIX = "${";
    public static final String PLACEHOLDER_SUFFIX = "}";
    // Properties 属性文件路径
    private String location;

    public void setLocation(String location) {
        this.location = location;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 加载 properties 属性配置文件
        Properties properties = loadProperties();
        // 使用属性值替换占位符
        processProperties(beanFactory, properties);
        // 向容器中添加字符解析器，解析 @Value 注解
        StringValueResolver resolver = new PlaceholderResolvingStringValueResolver(properties);
        beanFactory.addEmbeddedValueResolver(resolver);
    }

    /**
     * 加载 properties 属性配置文件
     *
     * @return java.util.Properties 结果
     * @version v1.0.0
     * @author lichao
     * @date 2024-03-19 17:58:53
     */
    private Properties loadProperties() {
        try {
            DefaultResourceLoader loader = new DefaultResourceLoader();
            Resource resource = loader.getResource(location);
            Properties properties = new Properties();
            properties.load(resource.getInputStream());
            return properties;
        } catch (IOException e) {
            throw new BeansException("导入属性文件失败：" + location, e);
        }
    }

    /**
     * 使用属性值替换占位符
     *
     * @param beanFactory Bean 容器
     * @param properties  属性值
     * @version v1.0.0
     * @author lichao
     * @date 2024-03-19 17:59:44
     */
    private void processProperties(ConfigurableListableBeanFactory beanFactory, Properties properties) {
        String[] beanDefinitionNames = beanFactory.getBeanDefinitionNames();
        for (String beanName : beanDefinitionNames) {
            BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
            resolvePropertyValues(beanDefinition, properties);
        }
    }

    /**
     * 使用属性值替换占位符
     *
     * @param beanDefinition BeanDefinition 信息
     * @param properties     属性值
     * @version v1.0.0
     * @author lichao
     * @date 2024-03-19 18:00:22
     */
    private void resolvePropertyValues(BeanDefinition beanDefinition, Properties properties) {
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        for (PropertyValue propertyValue : propertyValues.getPropertyValues()) {
            Object value = propertyValue.getValue();
            if (value instanceof String) {
                value = resolvePlaceholder((String) value, properties);
                propertyValues.addPropertyValue(new PropertyValue(propertyValue.getName(), value));
            }
        }
    }

    /**
     * 解析占位符中的字符串，使用属性值替换占位符
     *
     * @param value      字符串占位符
     * @param properties 属性值
     * @return java.lang.String 替换结果
     * @version v1.0.0
     * @author lichao
     * @date 2024-03-19 18:01:29
     */
    private String resolvePlaceholder(String value, Properties properties) {
        // 仅简单支撑一个占位符的格式
        String strVal = value;
        StringBuffer buffer = new StringBuffer(strVal);
        int startIndex = strVal.indexOf(PLACEHOLDER_PREFFIX);
        int endIndex = strVal.indexOf(PLACEHOLDER_SUFFIX);
        if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
            String propKey = strVal.substring(startIndex + 2, endIndex);
            String propValue = properties.getProperty(propKey);
            buffer.replace(startIndex, endIndex + 1, propValue);
        }
        return buffer.toString();
    }

    private class PlaceholderResolvingStringValueResolver implements StringValueResolver {
        private final Properties properties;

        public PlaceholderResolvingStringValueResolver(Properties properties) {
            this.properties = properties;
        }

        @Override
        public String resolveStringValue(String strVal) throws BeansException {
            return PropertyPlaceholderConfigurer.this.resolvePlaceholder(strVal, properties);
        }
    }

}

